#include "datamediavalidator.h"
#include <QDebug>
#include <QFile>
#include <QCryptographicHash>

#define MD5_PREFIX "#md5:"
#define BUFFER_SIZE (int)(32 + 1 + sizeof(MD5_PREFIX))

/**
 * @brief fakeCode 假校验
 * 用于某些情况下，可以手动修改文件的校验值，而不被程序认为是错误的。
 */
static QByteArray fakeCode;

DataMediaValidator::DataMediaValidator()
{
    if (fakeCode.isEmpty() || fakeCode.size() != 32)
    {
        fakeCode.clear();

        // fakecode will be:00112233445566778899aabbccddeeff
        QByteArray code="0123456789abcdef";
        for (int i = 0; i < 16; i++)
        {
            fakeCode.append(code.at(i));
            fakeCode.append(code.at(i));
        }
    }
}

QByteArray DataMediaValidator::removeVerifyCode(const QByteArray &array)
{
    if (array.size() < BUFFER_SIZE)
        return array;

    // 找到最后一个出现的位置，然后返回其左侧的数据即可；
    int index = array.lastIndexOf(MD5_PREFIX);
    if (index == -1)
        return array;

    return array.left(index);
}

void DataMediaValidator::appendVerifyCode(const QString &filename)
{
    QFile file(filename);
    if (!file.open(QIODevice::ReadWrite))
    {
        qWarning() << "Could not open the file " << filename << ", the error reason is : " << file.errorString();
    }

    QCryptographicHash algorithm(QCryptographicHash::Md5);
    algorithm.addData(file.readAll());
    file.seek(file.size());
    file.write(MD5_PREFIX + algorithm.result().toHex() + "\n"); // 在文件末尾添加md5
    file.close();;
}

bool DataMediaValidator::isValid(const QString &filename)
{
    QFile file(filename);
    if (!file.exists())
    {
        qWarning() << QString("The file %1 is not exist.").arg(filename);
        return false;
    }

    if (file.size() < BUFFER_SIZE)
        return false;

    if (!file.open(QIODevice::ReadOnly))
    {
        qWarning() << QString("The file %1 can't be opened.").arg(filename);
        return false;
    }

    QByteArray array = file.readAll();
    return isValid(array);
}

bool DataMediaValidator::isValid(const QByteArray &data)
{
    if (data.size() < BUFFER_SIZE)
        return false;

    // 找到MD5出现的位置，如果没有，则无效
    int index = data.lastIndexOf(MD5_PREFIX);
    if (index == -1)
    {
        qWarning() << "No \"#md5:\" string";
        return false;
    }

    // 末尾的md5数据串
    QByteArray verifyCodeString = data.mid(index);

    QCryptographicHash algorithm(QCryptographicHash::Md5);
    algorithm.addData(data.data(), index);
    // 计算出来的md5结果
    QByteArray result = algorithm.result().toHex().toLower();

    // 末尾包含计算的MD5，或者包含特定的字符串，认为是有效的。
    bool ret = verifyCodeString.contains(result) || verifyCodeString.contains(fakeCode);
    if (!ret)
        qWarning() << QString("The data check failded.");

    return ret;
}

bool DataMediaValidator::compare(const QString &filename1, const QString &filename2)
{
    if (!QFile::exists(filename1))
    {
        qWarning() << QString("The file %1 is not exist.").arg(filename1);
        return false;
    }

    if (!QFile::exists(filename2))
    {
        qWarning() << QString("The file %1 is not exist.").arg(filename2);
        return false;
    }

    QFile file1(filename1);
    QFile file2(filename2);
    if (file1.size() != file2.size())
        return false;

    if (!file1.open(QIODevice::ReadOnly))
    {
        qWarning() << QString("The file %1 can't be opened.").arg(filename1);
        return false;
    }

    if (!file2.open(QIODevice::ReadOnly))
    {
        qWarning() << QString("The file %1 can't be opened.").arg(filename2);
        return false;
    }

    // TODO, 如果文件很大呢，这个方法 。
    QByteArray array1 = file1.readAll();
    QByteArray array2 = file2.readAll();
    return array1 == array2;
}
